﻿using System;
using System.Linq;
using Kaleida.ServiceMonitor.Framework;
using NUnit.Framework;

namespace Kaleida.UnitTests.Framework
{
    [TestFixture]
    public class StringExtensionsTests
    {
        [Test]
        public void TestToTimeSpan()
        {
            Assert.AreEqual(TimeSpan.FromMilliseconds(120), "120ms".ToTimeSpan());
            Assert.AreEqual(TimeSpan.FromMilliseconds(-120), "-120ms".ToTimeSpan());
            Assert.AreEqual(TimeSpan.FromMilliseconds(120), "120".ToTimeSpan());

            Assert.AreEqual(TimeSpan.FromDays(4.5), "4.5d".ToTimeSpan());
            Assert.AreEqual(TimeSpan.FromHours(4.5), "4.5h".ToTimeSpan());
            Assert.AreEqual(TimeSpan.FromMinutes(4.5), "4.5m".ToTimeSpan());
            Assert.AreEqual(TimeSpan.FromSeconds(4.5), "4.5s".ToTimeSpan());
            Assert.AreEqual(TimeSpan.FromMilliseconds(4.5), "4.5ms".ToTimeSpan());
        }

        [Test]
        public void TestIsTimeSpan()
        {
            Assert.IsTrue("120ms".IsTimeSpan());
            Assert.IsTrue("-120ms".IsTimeSpan());
            Assert.IsTrue("120".IsTimeSpan());
            Assert.IsTrue("4.5d".IsTimeSpan());
            Assert.IsTrue("4.5h".IsTimeSpan());
            Assert.IsTrue("4.5m".IsTimeSpan());
            Assert.IsTrue("4.5s".IsTimeSpan());
            Assert.IsTrue("4.5ms".IsTimeSpan());

            Assert.IsFalse("4.5mi".IsTimeSpan());
            Assert.IsFalse("A4.5m".IsTimeSpan());
        }

        [Test]
        [ExpectedException(typeof(FormatException), ExpectedMessage = "Cannot parse '4.5x' as a duration. The following suffices may be used: day, hr, min, sec, ms")]
        public void TestExceptionIsThrownIfInvalidSuffix()
        {
            "4.5x".ToTimeSpan();
        }

        [Test]
        public void TestTimespanToFormattedString()
        {
            Assert.AreEqual("[01:02:03]", new TimeSpan(1, 2, 3).ToFormattedString());
            Assert.AreEqual("[23:59:59]", new TimeSpan(23, 59, 59).ToFormattedString());
            Assert.AreEqual("[3 23:59:59]", new TimeSpan(3, 23, 59, 59).ToFormattedString());
            Assert.AreEqual("[3 23:59:59]", new TimeSpan(3, 23, 59, 59, 0).ToFormattedString());
            Assert.AreEqual("[3 23:59:59.001]", new TimeSpan(3, 23, 59, 59, 1).ToFormattedString());
            Assert.AreEqual("-[3 23:59:59.999]", new TimeSpan(3, 23, 59, 59, 999).Negate().ToFormattedString());
        }

        [Test]
        public void TestStringToFormattedString()
        {
            Assert.AreEqual("''", "".ToFormattedString());
            Assert.AreEqual("'abc'", "abc".ToFormattedString());
        }

        [Test]
        public void TestSplitIntoKeyValuePairs()
        {
            Assert.AreEqual(0, "".SplitIntoKeyValuePairs().Count());

            Assert.AreEqual(2, "a=1,b=2".SplitIntoKeyValuePairs().Count());
            Assert.AreEqual("a", "a=1,b=2".SplitIntoKeyValuePairs()[0].Key);
            Assert.AreEqual("1", "a=1,b=2".SplitIntoKeyValuePairs()[0].Value);
            Assert.AreEqual("b", "a=1,b=2".SplitIntoKeyValuePairs()[1].Key);
            Assert.AreEqual("2", "a=1,b=2".SplitIntoKeyValuePairs()[1].Value);
        }

        [Test, ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Expected argument 'a' to be specified as ARG=VAL")]
        public void TestSplitIntoKeyValuePairsThrowsExceptionIfBadlyFormed()
        {
            "a,b=2".SplitIntoKeyValuePairs(); //throws
        }

    	[Test]
		public void TestCanParseOneEmailAsMailAddressList()
    	{
    		var list = "me@example.com".ToMailAddressList();
			Assert.AreEqual(1, list.Count);
			Assert.AreEqual("me@example.com", list[0].ToString());
    	}

		[Test]
		public void TestCanParseMultipleEmailAsMailAddressList()
		{
			var list = "me@example.com;you@example.com;them@example.com".ToMailAddressList();
			Assert.AreEqual(3, list.Count);
			Assert.AreEqual("me@example.com", list[0].ToString());
			Assert.AreEqual("you@example.com", list[1].ToString());
			Assert.AreEqual("them@example.com", list[2].ToString());
		}

		[Test]
		public void TestSpacesBetweenEmailAddressesAreNotImportantWhenParsingMailAddressList()
		{
			var list = "   me@example.com ; you@example.com  ;    them@example.com   ".ToMailAddressList();
			Assert.AreEqual(3, list.Count);
			Assert.AreEqual("me@example.com", list[0].ToString());
			Assert.AreEqual("you@example.com", list[1].ToString());
			Assert.AreEqual("them@example.com", list[2].ToString());
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Must provide at least one email address (multiple addresses can be specified using a semicolon. e.g. \"me@example.com;someone@example.com\")")]
		public void TestParseEmptyStringAsMailAddressListThrowsException()
		{
			"".ToMailAddressList(); // throws
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "The specified string is not in the form required for an e-mail address. (multiple addresses can be specified using a semicolon. e.g. \"me@example.com;someone@example.com\")")]
		public void TestParseInvalidMailAddressThrowsException()
		{
			"nonvalidEmail".ToMailAddressList(); // throws
		}

    	[Test]
		public void TestCanParseScheduledTime()
    	{
    		Assert.AreEqual(9, "09:32".ToScheduledTime().Hour);
    		Assert.AreEqual(32, "09:32".ToScheduledTime().Minute);

			Assert.AreEqual(9, "9:32".ToScheduledTime().Hour);
			Assert.AreEqual(32, "9:32".ToScheduledTime().Minute);
    	}

        [Test]
        public void TestToScheduledTimeListCanParseSingleScheduledTimes()
        {
            var list = "14:30".ToScheduledTimeList();
            Assert.AreEqual(1, list.Count);
            Assert.AreEqual(14, list[0].Hour);
            Assert.AreEqual(30, list[0].Minute);
        }

        [Test]
        public void TestToScheduledTimeListCanParseMultipleScheduledTime()
        {
            var list = "14:30;15:32".ToScheduledTimeList();
            Assert.AreEqual(2, list.Count);
            Assert.AreEqual(14, list[0].Hour);
            Assert.AreEqual(30, list[0].Minute);
            Assert.AreEqual(15, list[1].Hour);
            Assert.AreEqual(32, list[1].Minute);
        }

		[Test]
		[ExpectedException(typeof(FormatException), ExpectedMessage = "Cannot parse '0932' as a ScheduledTime. Expected string to be in HH:MM format. E.g. 09:32")]
		public void TestExceptionIsThrownIfScheduledTimeNotCorrectFormat()
		{
			"0932".ToScheduledTime(); // throws
		}

		[Test]
		[ExpectedException(typeof(FormatException), ExpectedMessage = "Cannot parse 'a1:32' as a ScheduledTime. Expected hour to be a number")]
		public void TestExceptionIsThrownIfScheduledTimeHourNotInteger()
		{
			"a1:32".ToScheduledTime(); // throws
		}

		[Test]
		[ExpectedException(typeof(FormatException), ExpectedMessage = "Cannot parse '1:a2' as a ScheduledTime. Expected minute to be a number")]
		public void TestExceptionIsThrownIfScheduledTimeMinuteNotInteger()
		{
			"1:a2".ToScheduledTime(); // throws
		}

        [Test]
        public void TestJoinWith()
        {
            Assert.AreEqual("", new string[0].JoinWith(", "));
            Assert.AreEqual("cat", new[] { "cat" }.JoinWith(", "));
            Assert.AreEqual("cat, dog", new[] { "cat", "dog" }.JoinWith(", "));
            Assert.AreEqual("cat, dog, mouse", new[] {"cat", "dog", "mouse"}.JoinWith(", "));

            Assert.AreEqual("", new string[0].JoinWith(", ", " and "));
            Assert.AreEqual("cat", new[] { "cat" }.JoinWith(", ", " and "));
            Assert.AreEqual("cat and dog", new[] { "cat", "dog" }.JoinWith(", ", " and "));
            Assert.AreEqual("cat, dog and mouse", new[] { "cat", "dog", "mouse" }.JoinWith(", ", " and "));
        }

    }
}
